﻿using System;
using System.Windows.Input;

namespace Gmantis.Controls
{
    public delegate void CanExecuteRoutedEventHandler(object sender, CanExecuteRoutedEventArgs e);
    public delegate void ExecutedRoutedEventHandler(object sender, ExecutedRoutedEventArgs e);

    public class CommandBinding
    {
        // Fields
        private ICommand _command;        

        // Events
        public event CanExecuteRoutedEventHandler CanExecute;

        public event ExecutedRoutedEventHandler Executed;

        public event CanExecuteRoutedEventHandler PreviewCanExecute;

        public event ExecutedRoutedEventHandler PreviewExecuted;

        // Methods
        public CommandBinding()
        {
        }

        public CommandBinding(ICommand command)
            : this(command, null, null)
        {
        }

        public CommandBinding(ICommand command, ExecutedRoutedEventHandler executed)
            : this(command, executed, null)
        {
        }

        public CommandBinding(ICommand command, ExecutedRoutedEventHandler executed, CanExecuteRoutedEventHandler canExecute)
        {
            if (command == null)
            {
                throw new ArgumentNullException("command");
            }
            this._command = command;
            if (executed != null)
            {
                this.Executed = (ExecutedRoutedEventHandler)Delegate.Combine(this.Executed, executed);
            }
            if (canExecute != null)
            {
                this.CanExecute = (CanExecuteRoutedEventHandler)Delegate.Combine(this.CanExecute, canExecute);
            }
        }

        private bool CheckCanExecute(object sender, ExecutedRoutedEventArgs e)
        {
            CanExecuteRoutedEventArgs args = new CanExecuteRoutedEventArgs(e.Command, e.Parameter)
            {
                EventName = "CanExecuteEvent",
                OriginalSource = sender
            };
            this.OnCanExecute(sender, args);
            return args.CanExecute;
        }

        internal void OnCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            if (!e.Handled)
            {
                if (e.EventName == "CanExecuteEvent")
                {
                    if (this.CanExecute != null)
                    {
                        this.CanExecute(sender, e);
                        if (e.CanExecute)
                        {
                            e.Handled = true;
                        }
                    }
                    else if (!e.CanExecute && (this.Executed != null))
                    {
                        e.CanExecute = true;
                        e.Handled = true;
                    }
                }
                else if (this.PreviewCanExecute != null)
                {
                    this.PreviewCanExecute(sender, e);
                    if (e.CanExecute)
                    {
                        e.Handled = true;
                    }
                }
            }
        }

        internal void OnExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            if (!e.Handled)
            {
                if (e.EventName == "ExecutedEvent")
                {
                    if ((this.Executed != null) && this.CheckCanExecute(sender, e))
                    {
                        this.Executed(sender, e);
                        e.Handled = true;
                    }
                }
                else if ((this.PreviewExecuted != null) && this.CheckCanExecute(sender, e))
                {
                    this.PreviewExecuted(sender, e);
                    e.Handled = true;
                }
            }
        }

        // Properties
        public ICommand Command
        {
            get
            {
                return this._command;
            }
            set
            {
                if (value == null)
                {
                    throw new ArgumentNullException("value");
                }
                this._command = value;
            }
        }
    }
}
